package com.hccl.service.gate;
import com.hccl.mongoEntities.GateAnalyseResultEntity;
import com.hccl.mongoEntities.GrammarVariableEntity;
import entities.SLUResult;
import gate.*;
import gate.corpora.DocumentContentImpl;
import gate.creole.ExecutionException;
import gate.creole.SerialAnalyserController;
import gate.creole.gazetteer.AbstractGazetteer;
import java.io.File;
import java.util.*;

import static com.hccl.config.Constants.gappUrl;

/**
 * Created by sunjia on 2018/4/9.
 * 功能：Gate匹配返回ArrayList<SLUResult>
 * 注意：其中包含了冲突回复的功能，目前统计方法没有用到
 */
public class GateMatcher {

    private SerialAnalyserController analyser;
    private Document gateDoc;
    private DefaultFunction defaultFunction;


    public GateMatcher(List<GrammarVariableEntity> grammarVariables) throws Exception {
        this.analyser = (SerialAnalyserController) gate.util.persistence.PersistenceManager
                .loadObjectFromFile(new File(gappUrl));
        this.defaultFunction = new DefaultFunction(getAbstractGazetteer(), grammarVariables);
        Corpus corpus = Factory.newCorpus("BatchProcessApp Corpus");
        this.gateDoc = Factory.newDocument("");
        this.gateDoc.setParameterValue("encoding", "UTF-8");
        corpus.add(this.gateDoc);
        this.analyser.setCorpus(corpus);
    }

    //Gate规则匹配流程性函数
    public GateAnalyseResultEntity gateAnalyse(String input) throws Exception {

        AnnotationSet as = gateMatch(input);
        Set<Annotation> currentGaz = getGazetteer(gateDoc);
        Set<Annotation> setAnnotationFilterbyLengthandSlotNum = filterGateResult(as);
        ArrayList<SLUResult> gateResults = getSLUResultformGate(setAnnotationFilterbyLengthandSlotNum);
        String conflictReply = checkVariable(setAnnotationFilterbyLengthandSlotNum);// 若筛选后还有多个，合成多个候选回复
        ArrayList<SLUResult> finalResultsfromGate = combineGateResult(gateResults);
        return new GateAnalyseResultEntity(finalResultsfromGate, conflictReply, currentGaz);

    }

    //执行Gate匹配，得到匹配结果
    public AnnotationSet gateMatch(String input) throws ExecutionException {
        gateDoc.setContent(new DocumentContentImpl(input));
        analyser.execute();
        AnnotationSet as = gateDoc.getAnnotations("Candidates");
        return as;
    }

    private Set<Annotation> getGazetteer(Document doc)
    {
        Set<Annotation> currentGaz = new HashSet<>();
        AnnotationSet annotations = doc.getAnnotations("Gazetteer");
        for (Annotation as:annotations){
            if(!as.getType().equals("Token")){
                currentGaz.add(as);
            }
        }
        return currentGaz;
    }

    //根据有效长度EffectiveLength和语义槽个数SlotNum进行筛选
    private Set<Annotation> filterGateResult(AnnotationSet as) {
        //只保留最大的maxEffectiveLength
        double maxEffectiveLength = 0.0;
        for (Annotation a : as) {
            double curEffectiveLength = (double) a.getFeatures().get("effective_length");
            if (curEffectiveLength > maxEffectiveLength)
                maxEffectiveLength = curEffectiveLength;
        }
        Set<Annotation> setAnnotationFilterbyLength = new HashSet<>();
        for (Annotation a : as) {
            double curEffectiveLength = (double) a.getFeatures().get("effective_length");
            if (Math.abs(curEffectiveLength - maxEffectiveLength) < 1e-20)
                setAnnotationFilterbyLength.add(a);
        }
        int minSlotNum = Integer.MAX_VALUE;
        for (Annotation i : setAnnotationFilterbyLength) {
            int curSlotNum = (int) i.getFeatures().get("slotNum");
            if (curSlotNum < minSlotNum)
                minSlotNum = curSlotNum;
        }
        //在maxEffectiveLength相等的情况下保留minSlotNum最少的
        Set<Annotation> setAnnotationFilterbySlotNum = new HashSet<>();
        for (Annotation i : setAnnotationFilterbyLength) {
            int curSlotNum = (int) i.getFeatures().get("slotNum");
            if (curSlotNum == minSlotNum)
                setAnnotationFilterbySlotNum.add(i);
        }
        return setAnnotationFilterbySlotNum;
    }

    //Gate若有多个结果合成冲突回复，但目前统计方法用不到
    public ArrayList<SLUResult> getSLUResultformGate(Set<Annotation> setAnnotationFilterbySlotNum) {

        ArrayList<SLUResult> res = new ArrayList<>();
        Set<SLUResult> matchSluResults = new HashSet<>();
        Set<SLUResult> noMatchSluResults = new HashSet<>();
        for (Annotation i : setAnnotationFilterbySlotNum) {
            String ruleName = (String) i.getFeatures().get("rule");
            String ruleType = (String) i.getFeatures().get("type");
            double curEffectiveLength = ((double) i.getFeatures().get("effective_length"));
            FeatureMap oriFeatures = (FeatureMap) i.getFeatures().get("slots");
            boolean slotMatch = true;
            if (defaultFunction != null) {//默认赋值函数的功能是？
                FeatureMap featureMapAdd = defaultFunction.assignRelatedVal(oriFeatures);
                for (Object key : featureMapAdd.keySet()) {
                    if (!oriFeatures.containsKey(key))
                        oriFeatures.put(key, featureMapAdd.get(key));
                    else if (!((FeatureMap) oriFeatures.get(key)).get("抽取名").
                            equals(((FeatureMap) featureMapAdd.get(key)).get("抽取名")))
                        slotMatch = false;
                }
            }
            Map<String, String> slots = new HashMap<>();
            for (Object key : oriFeatures.keySet()) {
                String key1 = key.toString().replace("$", "");
                slots.put(key1, (String) ((FeatureMap) oriFeatures.get(key)).get("抽取名"));
            }
            if (slotMatch)
                matchSluResults.add(new SLUResult(ruleName, ruleType, curEffectiveLength, slots));
            else
                noMatchSluResults.add(new SLUResult(ruleName, ruleType, curEffectiveLength, slots));
        }
        if (matchSluResults.isEmpty())
            res.addAll(noMatchSluResults);
        else
            res.addAll(matchSluResults);
        return res;

    }

    //若有多个均是##下的slot时，应合并在一起
    public ArrayList<SLUResult> combineGateResult(ArrayList<SLUResult> gateResults) {
        ArrayList<SLUResult> gateResultsCom = new ArrayList<>();
        ArrayList<SLUResult> gateResultTmp = new ArrayList<>();//处理##情况
        for(SLUResult res : gateResults){
            if (!res.ruleName.equals("##")){
                gateResultsCom.add(res);
            }else{
                gateResultTmp.add(res);
            }
        }
        if (gateResultTmp.size()>0) {
            Map<String, String> slotsCom = new HashMap<>();
            for (SLUResult res : gateResultTmp) {
                for (String key : res.slots.keySet()) {
                    slotsCom.put(key, res.slots.get(key));
                }
            }
            if (slotsCom.size() > 0) {
                gateResultsCom.add(new SLUResult("##", "stateNode", slotsCom.size(), slotsCom));
            }
        }
        return gateResultsCom;
    }

    private String checkVariable(Set<Annotation> filterSet) {

        String str = "";
        Map<String, Set<String>> varMap = new HashMap<>();

        for (Annotation i : filterSet) {
            String action = (String) i.getFeatures().get("command");
            FeatureMap oriFeatures = (FeatureMap) i.getFeatures().get("slots");
            for (Object key : oriFeatures.keySet()) {
                String str_userinput = (String) ((FeatureMap) oriFeatures.get(key)).get("matchedString");
                String str_standardName = (String) ((FeatureMap) oriFeatures.get(key)).get("抽取名");
                Set<String> stringList = varMap.get(str_userinput);
                if (stringList == null) {
                    stringList = new HashSet<>();
                    varMap.put(str_userinput, stringList);
                }
                stringList.add(str_standardName);
            }
        }

        for (String str_userinput : varMap.keySet()) {
            if (varMap.get(str_userinput).size() > 1) {
                str = str_userinput + "可能是";
                String strJoin = JoinStr(varMap.get(str_userinput), "、");
                str += strJoin;
                str += "，请问您要选择哪一个？";
                break;
            }
        }
        return str;
    }

    private String JoinStr(Collection<String> strlist, String sep) {
        String str = "";
        for (String string : strlist) {
            str += string;
            str += sep;
        }
        if (strlist.size() > 0) {
            str = str.substring(0, str.length() - sep.length());
        }
        return str;
    }

    private List<AbstractGazetteer> getAbstractGazetteer() {
        List<AbstractGazetteer> listGaz = new ArrayList<>();
        Iterator<ProcessingResource> prIter = analyser.getPRs().iterator();
        while (prIter.hasNext()) {
            ProcessingResource pr = (ProcessingResource) prIter.next();
            if (AbstractGazetteer.class.isAssignableFrom(pr.getClass())) {
                listGaz.add((AbstractGazetteer) pr);
            }
        }
        return listGaz;
    }

    public SerialAnalyserController getAnalyser() {
        return analyser;
    }

    public void setAnalyser(SerialAnalyserController analyser) {
        this.analyser = analyser;
    }
}


